﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

using Subgurim.Controles;

namespace CorreoSocialWeb.Models
{
    [System.ComponentModel.DataAnnotations.Schema.Table("Envios")]
    public class Envio : CorreoSocialWeb.Models.IEnvioFormEditFields, IOrigenDestinoString, IOrigenDestinoLatLong
    {
        [Key]
        [ScaffoldColumn(false)]
        public int Id { get; set; }

        [ScaffoldColumn(false)]
        public int UsuarioId { get; set; }

        [Required]
        public string Origen { get; set; }

        [Required]
        public string Destino { get; set; }

        public int EstadoId { get; set; }

        [NotMapped]
        public string EstadoString 
        {
            get
            {
                return new EnumStringHelper().Get(this.EstadoEnu);
            }
        }

        [NotMapped]
        public EstadoDeEnvioEnu EstadoEnu
        {
            get 
            {
                return (EstadoDeEnvioEnu)this.EstadoId;
            }
            set
            {
                this.EstadoId = (int)value;
            }
        }

        [ScaffoldColumn(false)]
        [Required]
        [System.ComponentModel.DataAnnotations.Schema.ForeignKey("TamanoDePaquete")]
        public int TamanoDePaqueteId { get; set; }

        [ScaffoldColumn(false)]
        [Required]
        public double OrigenLatitud { get; set; }

        [ScaffoldColumn(false)]
        [Required]
        public double OrigenLongitud { get; set; }

        [ScaffoldColumn(false)]
        [Required]
        public double DestinoLatitud { get; set; }

        [ScaffoldColumn(false)]
        [Required]
        public double DestinoLongitud { get; set; }
        
        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        public GLatLng CoordenadasOrigen
        {
            get
            {
                return new GLatLng((double)this.OrigenLatitud, (double)this.OrigenLongitud);
            }
            set
            {
                this.OrigenLatitud = value.lat;
                this.OrigenLongitud = value.lng;
            }
        }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        public GLatLng CoordenadasDestino
        {
            get
            {
                return new GLatLng((double)this.DestinoLatitud, (double)this.DestinoLongitud);
            }
            set
            {
                this.DestinoLatitud = value.lat;
                this.DestinoLongitud = value.lng;
            }
        }

        public string FechaDeSalidaString
        {
            get
            {
                if (!this.FechaDeSalida.HasValue)
                    return "(sin preferencia)";

                return this.FechaDeSalida.Value.ToString("dddd dd/MM/yyyy");
            }
        }

        public bool PuedeBuscarRuta
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.SeleccionandoMensajero;
            }
        }
      
        public bool PuedeCambiarRuta
        {
            get
            {
                return false;
            }
        }
       
        public bool PuedeMarcarDespachado
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.DespachoPendiente;
            }
        }

        public bool PuedeMarcarRecibido
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.EnTransito;
            }
        }

        public void AsignarRuta(Ruta ruta)
        { 
        }

        public bool PuedeCancelarDespacho 
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.DespachoPendiente;
            }
        }

        public bool PuedeCancelarSolicitudDeRuta
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.MensajeroSolicitado;
            }
        }    

        public bool PuedeCancelarEnRuta
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.EnTransito;
            }
        }    
         
        [NotMapped]
        public bool PuedeBorrar
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.SeleccionandoMensajero;
            }
        }

        [NotMapped]
        public bool PuedeMostrarProgreso
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.EnTransito;
            }
        }

        public bool PuedeModificar
        {
            get
            {
                return this.EstadoEnu == EstadoDeEnvioEnu.SeleccionandoMensajero;
            }
        }    

        public string OrigenDestinoString
        {
            get
            {
                return new OrigenDestinoStringHelper().Get(this);
            }
        }

        [Range(0, 2359)]
        public int? HorarioDeSalida { get; set; }

        [Range(0, 2359)]
        public int? HorarioDeLlegada { get; set; }

        public DateTime? FechaDeSalida { get; set; }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        [Range(0, 23)]
        [DisplayFormat(DataFormatString = "{0:d2}")]
        public int? HoraDeLlegada
        {
            get
            {
                return (this.HorarioDeLlegada / 100);
            }
            set
            {
                this.HorarioDeLlegada = value * 100 + (this.MinutosDeLlegada ?? 0);
            }
        }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        [Range(0, 59)]
        [DisplayFormat(DataFormatString = "{0:d2}")]
        public int? MinutosDeLlegada
        {
            get
            {
                return this.HorarioDeLlegada % 100;
            }
            set
            {
                this.HorarioDeLlegada = this.HoraDeLlegada * 100 + (value?? 0);
            }
        }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        public string HorarioDeSalidaString
        {
            get
            {
                if (!this.HorarioDeSalida.HasValue)
                    return "(sin preferencia)";

                return string.Format("{0:d}:{1:d2}", this.HoraDeSalida, this.MinutosDeSalida);
            }
        }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        public string HorarioDeLlegadaString
        {
            get
            {
                if (!this.HorarioDeLlegada.HasValue)
                    return "(sin preferencia)";

                return string.Format("{0:d0}:{1:d2}", this.HoraDeLlegada, this.MinutosDeLlegada);
            }
        }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        [Range(0, 23)]
        [DisplayFormat(DataFormatString = "{0:d2}")]
        public int? HoraDeSalida
        {
            get
            {
                return (this.HorarioDeSalida / 100);
            }
            set
            {
                this.HorarioDeSalida = value * 100 + (this.MinutosDeSalida ?? 0);
            }
        }

        [System.ComponentModel.DataAnnotations.Schema.NotMapped]
        [Range(0, 59)]
        [DisplayFormat(DataFormatString = "{0:d2}")]
        public int? MinutosDeSalida
        {
            get
            {
                return this.HorarioDeSalida % 100;
            }
            set
            {
                this.HorarioDeSalida = this.HoraDeSalida * 100 + (value ?? 0);
            }
        }

        [ScaffoldColumn(false)]
        [Required]
        public bool Borrado { get; set; }

        [Association("Usuario", "UsuarioId", "Id")]
        public Usuario Usuario { get; set; }

        //[Required(ErrorMessage="Valor requerido.")]
        [Association("pepe", "TamañoDePaqueteId", "Id")]
        public TamanoDePaquete TamanoDePaquete { get; set; }

        [ScaffoldColumn(false)]
        public string TamañoDePaqueteNombre
        {
            get
            {
                return TamanoDePaquete != null ? TamanoDePaquete.Nombre : "(sin tamaño)";
            }
        }

        public string Notas { get; set; }

        public static List<RutaDeEnvioBusqueda> GetRutaDeRankingDeRutas(int usuarioActualId, int envioId, int rutaId)
        {
            var opcionesDePaginacion = new OpcionesDePaginacion();

            opcionesDePaginacion.UsaPaginacion = false;

            return GetRankingDeRutas(usuarioActualId, envioId, opcionesDePaginacion).Where(x => x.RutaId.Equals(rutaId)).ToList();
        }

        public static List<RutaDeEnvioBusqueda> GetRankingDeRutas(int usuarioActualId, int envioId, OpcionesDePaginacion opcionesDePaginacion)
        {
            var db = new CorreoSocialDbContext();

            var envio = db.Envios
                .Include(z=>z.TamanoDePaquete)
                .FirstOrDefault(x => x.Id.Equals(envioId));

            List<Ruta> rutas = db.Rutas
                .Include(a=>a.TamanoDePaquete)
                .Include(b=>b.Usuario)
                .Where(x => !x.Borrada && x.Activa && !x.UsuarioId.Equals(usuarioActualId)).ToList();

            List<RutaDeEnvioBusqueda> rutasDeEnvio = rutas
                .Select(x => new RutaDeEnvioBusqueda(envio, x))
                .Where(x => x.Cumple)
                .OrderBy(x => x.RankingDouble).ToList();

            var rank = 1;
            foreach (RutaDeEnvioBusqueda item in rutasDeEnvio)
            {
                item.SetRanking(rank);
                rank++;
            }

            opcionesDePaginacion.SetTotalCount(rutasDeEnvio.Count());

            return rutasDeEnvio
                .Skip(opcionesDePaginacion.ItemsASaltar)
                .Take(opcionesDePaginacion.ItemsATomar).ToList();
        }

        public static void CancelarSolicitudDeRuta(int envioId)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            // actualizo el envio
            var envio = db.Envios
                .Include(x => x.Rutas)
                .Include(x => x.Rutas.Select(y =>y.Envio))
                .Include(x => x.Rutas.Select(y =>y.Envio.Usuario))
                .Include(x => x.Rutas.Select(y =>y.Ruta))
                .Include(x => x.Rutas.Select(y =>y.Ruta.Usuario))
                .FirstOrDefault(x => x.Id.Equals(envioId));

            envio.EstadoEnu = EstadoDeEnvioEnu.SeleccionandoMensajero;

            // la guardo antes que cambie el estado;
            var rutaDeEnvioPendiente = envio.RutaDeEnvioPendiente;

            envio.RutaDeEnvioPendiente.EstadoEnu = EstadoDeRutaDeEnvio.Cancelada;

            rutaDeEnvioPendiente.Ruta.Usuario
                .MandarMailCancelacionDeSolicitudRutaAMensajero(rutaDeEnvioPendiente);

            db.SaveChanges();
        }

        public static void MarcarDespachado(int envioId)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            // actualizo el envio
            var envio = db.Envios
                .Include(x => x.Rutas)
                .Include(x => x.Rutas.Select(y => y.Envio))
                .Include(x => x.Rutas.Select(y => y.Envio.Usuario))
                .Include(x => x.Rutas.Select(y => y.Ruta))
                .Include(x => x.Rutas.Select(y => y.Ruta.Usuario))
                .FirstOrDefault(x => x.Id.Equals(envioId));

            envio.EstadoEnu = EstadoDeEnvioEnu.EnTransito;

            envio.RutaDeEnvioAceptada
                .Ruta.Usuario
                .MandarMailDespachoAMensajero(envio.RutaDeEnvioAceptada);

            db.SaveChanges();
        }

        public RutaDeEnvio RutaDeEnvioPendiente
        {
            get
            {
                return this.Rutas.FirstOrDefault(x => x.EstadoId.Equals((int)EstadoDeRutaDeEnvio.Solicitada));
            }
        }

        public RutaDeEnvio RutaDeEnvioAceptada
        {
            get
            {
                var ruta = this.Rutas.FirstOrDefault(x => x.EstadoId.Equals((int)EstadoDeRutaDeEnvio.Aceptada));

                if (ruta == null)
                    ruta = this.Rutas.FirstOrDefault(x => x.EstadoId.Equals((int)EstadoDeRutaDeEnvio.Finalizada));

                return ruta;
            }
        }

        public static void Borrar(int envioId)
        {
            var db = new CorreoSocialDbContext();

            var item = db.Envios.FirstOrDefault(x => x.Id.Equals(envioId));

            if (item == null)
                throw new Exception("Envío no encontrado");

            if (!item.PuedeBorrar)
                throw new Exception("No puede borrarse este envío. ¿Está siendo usado?");

            item.Borrado = true;
            item.EstadoEnu = EstadoDeEnvioEnu.Borrado;

            db.SaveChanges();
        }

        public static void SolicitarRuta(int envioId, int rutaId, string comentarios, string urlConfirmacion)
        {
            RutaDeEnvio rutaDeEnvio = RutaDeEnvio.InsertarNueva(envioId, rutaId, comentarios);

            urlConfirmacion = string.Format(urlConfirmacion, rutaDeEnvio.Id);

            rutaDeEnvio.Ruta.Usuario.MandarMailSolicitudRuta(rutaDeEnvio, urlConfirmacion);
        }

        public static Envio GetParaEditar(CorreoSocialDbContext db, int usuarioId, int envioId)
        {
            return db.Envios.FirstOrDefault(x => !x.Borrado && x.Id.Equals(envioId) && x.UsuarioId.Equals(usuarioId));
        }

        public static Envio GetParaEditar(int usuarioId, int envioId)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            return GetParaEditar(db, usuarioId, envioId);
        }

        public static Envio NuevoEnvio()
        {
            Envio item = new Envio();

            item.Id = 0;
            item.EstadoEnu = EstadoDeEnvioEnu.SeleccionandoMensajero;

            return item;
        }

        [Association("Rutas", "Id", "EnvioId")]
        public List<RutaDeEnvio> Rutas { get; set; }

        public static List<Envio> GetLista(int usuarioId, OpcionesDePaginacion opcionesDePaginacion)
        {
            CorreoSocialDbContext db = new CorreoSocialDbContext();

            var resultados = db.Envios
                .Include(x => x.TamanoDePaquete)
                .Include(x => x.Rutas)
                .Include(x => x.Rutas.Select(y => y.Ruta))
                .Include(x => x.Rutas.Select(y => y.Ruta.Usuario))
                .Where(x => x.UsuarioId.Equals(usuarioId) && !x.Borrado)
                .OrderByDescending(x => x.Id).ToList();

            opcionesDePaginacion.SetTotalCount(resultados.Count());

            return resultados
                .Skip(opcionesDePaginacion.ItemsASaltar)
                .Take(opcionesDePaginacion.ItemsATomar).ToList();
        }

        public bool PorLlegar
        {
            get
            { 
                if (this.EstadoEnu != EstadoDeEnvioEnu.EnTransito)
                    return false;

                if (this.RutaDeEnvioAceptada == null)
                    return false;

                return this.RutaDeEnvioAceptada.Progreso >= 90;
            }
        }

        [NotMapped]
        public string ProgresoEnvioString
        {
            get
            {
                if (this.EstadoEnu != EstadoDeEnvioEnu.EnTransito)
                    return "N/A";

                if (this.RutaDeEnvioAceptada == null)
                    return "(ruta de envío no encontrada)";

                return this.RutaDeEnvioAceptada.ProgresoString;
            }
        }

        public static Envio GetPorId(int envioId)
        {
            return new CorreoSocialDbContext().Envios
                .Include(x => x.Rutas)
                .Include(x => x.Rutas.Select(y => y.Envio))
                .Include(x => x.Rutas.Select(y => y.Envio.Usuario))
                .Include(x => x.Rutas.Select(y => y.Ruta))
                .Include(x => x.Rutas.Select(y => y.Ruta.Usuario))
                .FirstOrDefault(x => x.Id.Equals(envioId));
        }

        [NotMapped]
        public string HoraDeSalidaString
        {
            get
            {
                return string.Format("{0:d2}", this.HoraDeSalida);
            }
        }

        [NotMapped]
        public string HoraDeLlegadaString
        {
            get
            {
                return string.Format("{0:d2}", this.HoraDeLlegada);
            }
        }

        [NotMapped]
        public string MinutosDeSalidaString
        {
            get
            {
                return string.Format("{0:d2}", this.MinutosDeSalida);
            }
        }

        [NotMapped]
        public string MinutosDeLlegadaString
        {
            get
            {
                return string.Format("{0:d2}", this.MinutosDeLlegada);
            }
        }

        [NotMapped]
        public bool EsNuevo
        {
            get
            {
                return this.Id < 1;
            }
        }

        [NotMapped]
        public bool TieneRutaActual
        {
            get
            {
                return this.RutaActual != null;
            }
        }

        [NotMapped]
        public RutaDeEnvio RutaActual
        {
            get
            {
                if (this.RutaDeEnvioAceptada != null)
                    return this.RutaDeEnvioAceptada;

                return this.RutaDeEnvioPendiente;
            }
        }
    }
}